package com.xlongwei.light4j.util;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 高效GUID产生算法(sequence),基于Snowflake实现64位自增ID算法。 <br>
 * 优化开源项目 http://git.oschina.net/yu120/sequence
 * </p>
 *
 * @author hubin
 * @Date 2016-08-01
 */
public class IdWorker {

    /**
     * 主机和进程的机器码
     */
    private static final Sequence WORKER = new Sequence();

    public static long getId() {
        return WORKER.nextId();
    }

    /**
     * <p>
     * 高并发场景下System.currentTimeMillis()的性能问题的优化
     * </p>
     * <p>
     * System.currentTimeMillis()的调用比new一个普通对象要耗时的多（具体耗时高出多少我还没测试过，有人说是100倍左右）<br>
     * System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<br>
     * 后台定时更新时钟，JVM退出时，线程自动回收<br>
     * 10亿：43410,206,210.72815533980582%<br>
     * 1亿：4699,29,162.0344827586207%<br>
     * 1000万：480,12,40.0%<br>
     * 100万：50,10,5.0%<br>
     * </p>
     *
     * @author hubin
     * @Date 2016-08-01
     */
    public static class SystemClock {

        private final long period;
        private final AtomicLong now;

        private SystemClock(long period) {
            this.period = period;
            this.now = new AtomicLong(System.currentTimeMillis());
            scheduleClockUpdating();
        }

        private static SystemClock instance() {
            return InstanceHolder.INSTANCE;
        }

        public static long now() {
            return instance().currentTimeMillis();
        }

        public static Date date() {
            return new Date(instance().currentTimeMillis());
        }

        private void scheduleClockUpdating() {
            TaskUtil.scheduleAtFixedRate(() -> {
                    now.set(System.currentTimeMillis());
            }, period, period, TimeUnit.MILLISECONDS);
        }

        private long currentTimeMillis() {
            return now.get();
        }

        private static class InstanceHolder {
            public static final SystemClock INSTANCE = new SystemClock(1);
        }

    }
    
	/**
	 * <p>
	 * 分布式高效有序ID生产黑科技(sequence) <br>
	 * 优化开源项目：http://git.oschina.net/yu120/sequence
	 * </p>
	 *
	 * @author hubin
	 * @date 2016-08-18
	 */
    @Slf4j
	public static class Sequence {
	    /** 时间起始标记点，作为基准，一般取系统的最近时间（一旦确定不能变动） */
	    private final long twepoch = 1288834974657L;
	    /** 机器标识位数 */
	    private final long workerIdBits = 5L;
	    private final long datacenterIdBits = 5L;
	    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
	    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
	    /** 毫秒内自增位 */
	    private final long sequenceBits = 12L;
	    private final long workerIdShift = sequenceBits;
	    private final long datacenterIdShift = sequenceBits + workerIdBits;
	    /** 时间戳左移动位 */
	    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
	    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
	
	    private long workerId;
	
	    /** 数据标识id部分 */
	    private long datacenterId;
	    /** 0，并发控制 */
	    private long sequence = 0L;
	    /** 上次生产id时间戳 */
	    private long lastTimestamp = -1L;
	
	    public Sequence() {
	        this.datacenterId = getDatacenterId(maxDatacenterId);
	        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
	    }
	
	    /**
	     * @param workerId     工作机器ID
	     * @param datacenterId 序列号
	     */
	    public Sequence(long workerId, long datacenterId) {
	        if (workerId > maxWorkerId || workerId < 0) {
	            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
	        }
	        if (datacenterId > maxDatacenterId || datacenterId < 0) {
	            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
	        }
	        this.workerId = workerId;
	        this.datacenterId = datacenterId;
	    }
	
	    /**
	     * <p>
	     * 获取 maxWorkerId
	     * </p>
	     */
	    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
	        StringBuilder mpid = new StringBuilder();
	        mpid.append(datacenterId);
	        String name = ManagementFactory.getRuntimeMXBean().getName();
	        if (StringUtil.hasLength(name)) {
	            /*
	             * GET jvmPid
				 */
	            mpid.append(name.split("@")[0]);
	        }
	        /*
	         * MAC + PID 的 hashcode 获取16个低位
			 */
	        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
	    }
	
	    /**
	     * <p>
	     * 数据标识id部分
	     * </p>
	     */
	    protected static long getDatacenterId(long maxDatacenterId) {
	        long id = 0L;
	        try {
	            InetAddress ip = InetAddress.getLocalHost();
	            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
	            if (network == null) {
	                id = 1L;
	            } else {
	                byte[] mac = network.getHardwareAddress();
	                if (null != mac) {
	                    id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
	                    id = id % (maxDatacenterId + 1);
	                }
	            }
	        } catch (Exception e) {
	            log.warn(" getDatacenterId: " + e.getMessage());
	        }
	        return id;
	    }
	
	    /**
	     * 获取下一个ID
	     *
	     * @return
	     */
	    public synchronized long nextId() {
	        long timestamp = timeGen();
//	        if (timestamp < lastTimestamp) {//闰秒
//	            long offset = lastTimestamp - timestamp;
//	            if (offset <= 5) {
//	                try {
//	                    wait(offset << 1);
//	                    timestamp = timeGen();
//	                    if (timestamp < lastTimestamp) {
//	                        throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
//	                    }
//	                } catch (Exception e) {
//	                    throw new RuntimeException(e);
//	                }
//	            } else {
//	                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
//	            }
//	        }
	
	        if (lastTimestamp == timestamp) {
	            // 相同毫秒内，序列号自增
	            sequence = (sequence + 1) & sequenceMask;
	            if (sequence == 0) {
	                // 同一毫秒的序列数已经达到最大
	                timestamp = tilNextMillis(lastTimestamp);
	            }
	        } else {
	            // 不同毫秒内，序列号置为 1 - 3 随机数
	            sequence = ThreadLocalRandom.current().nextLong(1, 3);
	        }
	
	        lastTimestamp = timestamp;
	
	        // 时间戳部分 数据中心部分 机器标识部分 序列号部分
	        return ((timestamp - twepoch) << timestampLeftShift) 
	                | (datacenterId << datacenterIdShift)
	                | (workerId << workerIdShift)
	                | sequence;
	    }
	
	    protected long tilNextMillis(long lastTimestamp) {
	        long timestamp = timeGen();
	        while (timestamp <= lastTimestamp) {
	            timestamp = timeGen();
	        }
	        return timestamp;
	    }
	
	    protected long timeGen() {
	        return SystemClock.now();
	    }
	
	}
}